import os
import re
import time
import json
import base64
import hashlib
import rsa
from datetime import datetime
import requests
import concurrent.futures
import xml.etree.ElementTree as ET
from pathlib import Path

# 并发次数，开了可能封号，默认为1
Max = 50

BI_RM = list("0123456789abcdefghijklmnopqrstuvwxyz")
B64MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

# Token缓存文件路径
TOKEN_CACHE_FILE = Path("CtyunPan.json")

global time_now


def save_token_cache(username, session):
    """保存Token到缓存文件"""
    if not TOKEN_CACHE_FILE.exists():
        cache = {}
    else:
        try:
            with open(TOKEN_CACHE_FILE, "r", encoding='utf-8') as f:
                cache = json.load(f)
        except:
            cache = {}

    cache[username] = {
        "cookies": session.cookies.get_dict(),
        "timestamp": int(time.time())
    }

    with open(TOKEN_CACHE_FILE, "w", encoding='utf-8') as f:
        json.dump(cache, f, ensure_ascii=False, indent=2)


def load_token_cache(username):
    """从缓存文件加载Token"""
    if not TOKEN_CACHE_FILE.exists():
        return None

    try:
        with open(TOKEN_CACHE_FILE, "r", encoding='utf-8') as f:
            cache = json.load(f)
    except:
        return None

    if username not in cache:
        return None

    # 检查Token是否过期（假设7天有效期）
    if time.time() - cache[username]["timestamp"] > 7 * 24 * 3600:
        return None

    session = requests.Session()
    session.cookies.update(cache[username]["cookies"])

    # 验证Token是否仍然有效
    test_url = "https://cloud.189.cn/api/open/user/getUserInfoForPortal.action"
    try:
        resp = session.get(test_url, timeout=5)
        if resp.status_code == 200:
            return session
    except:
        pass

    return None


def int2char(a):
    return BI_RM[a]


def b64tohex(a):
    d = ""
    e = 0
    c = 0
    for i in range(len(a)):
        if list(a)[i] != "=":
            v = B64MAP.index(list(a)[i])
            if 0 == e:
                e = 1
                d += int2char(v >> 2)
                c = 3 & v
            elif 1 == e:
                e = 2
                d += int2char(c << 2 | v >> 4)
                c = 15 & v
            elif 2 == e:
                e = 3
                d += int2char(c)
                d += int2char(v >> 2)
                c = 3 & v
            else:
                e = 0
                d += int2char(c << 2 | v >> 4)
                d += int2char(15 & v)
    if e == 1:
        d += int2char(c << 2)
    return d


def rsa_encode(j_rsakey, string):
    rsa_key = f"-----BEGIN PUBLIC KEY-----\n{j_rsakey}\n-----END PUBLIC KEY-----"
    pubkey = rsa.PublicKey.load_pkcs1_openssl_pem(rsa_key.encode())
    encrypted = rsa.encrypt(f'{string}'.encode(), pubkey)
    base64_encoded = base64.b64encode(encrypted).decode()
    result = b64tohex(base64_encoded)
    return result


def login(username, password):
    # 先尝试从缓存加载
    session = load_token_cache(username)
    if session:
        print(f"[账号: {username[:2]}****{username[7:]}] 使用缓存Token登录")
        return session

    print(f"[账号: {username[:2]}****{username[7:]}] 正在使用账号密码登录...")
    urlToken = "https://m.cloud.189.cn/udb/udb_login.jsp?pageId=1&pageKey=default&clientType=wap&redirectURL=https://m.cloud.189.cn/zhuanti/2021/shakeLottery/index.html"
    s = requests.Session()
    r = s.get(urlToken)
    pattern = r"https?://[^\s'\"]+"
    match = re.search(pattern, r.text)
    if match:
        url = match.group()
    else:
        print(f"[账号: {username[:2]}****{username[7:]}] 没有找到url")
        return None

    r = s.get(url)
    pattern = r"<a id=\"j-tab-login-link\"[^>]*href=\"([^\"]+)\""
    match = re.search(pattern, r.text)
    if match:
        href = match.group(1)
    else:
        print(f"[账号: {username[:2]}****{username[7:]}] 没有找到href链接")
        return None

    r = s.get(href)
    captchaToken = re.findall(r"captchaToken' value='(.+?)'", r.text)[0]
    lt = re.findall(r'lt = "(.+?)"', r.text)[0]
    returnUrl = re.findall(r"returnUrl= '(.+?)'", r.text)[0]
    paramId = re.findall(r'paramId = "(.+?)"', r.text)[0]
    j_rsakey = re.findall(r'j_rsaKey" value="(\S+)"', r.text, re.M)[0]
    s.headers.update({"lt": lt})

    username_enc = rsa_encode(j_rsakey, username)
    password_enc = rsa_encode(j_rsakey, password)
    url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0',
        'Referer': 'https://open.e.189.cn/',
    }
    data = {
        "appKey": "cloud",
        "accountType": '01',
        "userName": f"{{RSA}}{username_enc}",
        "password": f"{{RSA}}{password_enc}",
        "validateCode": "",
        "captchaToken": captchaToken,
        "returnUrl": returnUrl,
        "mailSuffix": "@189.cn",
        "paramId": paramId
    }
    try:
        r = s.post(url, data=data, headers=headers, timeout=10)
        if r.json().get('result', -1) == 0:
            redirect_url = r.json()['toUrl']
            r = s.get(redirect_url)
            # 登录成功后保存Token
            save_token_cache(username, s)
            return s
        else:
            print(f"[账号: {username[:2]}****{username[7:]}] 登录失败: {r.json().get('msg', '未知错误')}")
            return None
    except Exception as e:
        print(f"[账号: {username[:2]}****{username[7:]}] 登录请求异常: {str(e)}")
        return None


def sign_task(session, attempt, username):
    global time_now
    rand = str(round(time_now * 1000))
    surl = f'https://api.cloud.189.cn/mkt/userSign.action?rand={rand}&clientType=TELEANDROID&version=8.6.3&model=SM-G930K'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; SM-G930K Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36 Ecloud/8.6.3 Android/22 clientId/355325117317828 clientModel/SM-G930K imsi/460071114317824 clientChannelId/qq proVersion/1.0.6',
        "Referer": "https://m.cloud.189.cn/zhuanti/2016/sign/index.jsp?albumBackupOpened=1",
        "Host": "m.cloud.189.cn",
        "Accept-Encoding": "gzip, deflate",
    }
    try:
        response = session.get(surl, headers=headers, timeout=10)
        #print(response.json())
        if response.status_code == 200:
            netdiskBonus = response.json().get('netdiskBonus')
            #issign =  response.json().get('isSign')
            try:
                signtime = datetime.timestamp(datetime.fromisoformat(str(response.json().get('signTime'))))
            except Exception as e:
                signtime = 0
            detla_time = time_now - signtime
            if detla_time < 0.5:
                return True, netdiskBonus
            else:
                return False, 0
        else:
            print(f"[账号: {username[:2]}****{username[7:]}] 签到请求失败，状态码: {response.status_code}")
            return False, 0
    except Exception as e:
        print(f"[账号: {username[:2]}****{username[7:]}] 签到请求异常: {str(e)}")
        return False, 0


def get_capacity(session, username):
    url = "https://cloud.189.cn/api/open/user/getUserInfoForPortal.action"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; SM-G930K Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36 Ecloud/8.6.3 Android/22 clientId/355325117317828 clientModel/SM-G930K imsi/460071114317824 clientChannelId/qq proVersion/1.0.6',
        "Host": "cloud.189.cn",
        "Accept-Encoding": "gzip, deflate",
    }
    try:
        response = session.get(url, headers=headers, timeout=10)

        # 尝试解析XML响应
        try:
            root = ET.fromstring(response.text)
            capacity = int(root.find('capacity').text) if root.find('capacity') is not None else 0
            available = int(root.find('available').text) if root.find('available') is not None else 0

            # 计算已使用容量
            used = capacity - available
            personal_used = used / 1024 / 1024
            personal_total = capacity / 1024 / 1024
            return (personal_used, personal_total)
        except ET.ParseError:
            # 如果不是XML，尝试解析JSON
            data = response.json()
            capacity = data.get('capacity', 0)
            available = data.get('available', 0)

            # 计算已使用容量
            used = capacity - available
            personal_used = round(used / 1024 / 1024 / 1024, 2)
            personal_total = round(capacity / 1024 / 1024 / 1024, 2)
            return (personal_used, personal_total)

    except Exception as e:
        print(f"[账号: {username[:2]}****{username[7:]}] 获取容量信息异常: {str(e)}")
        return (0, 0)


def process_account(account):
    try:
        username, password = account.split('#', 1)  # 只分割第一个#号
    except ValueError:
        print(f"无效的账号格式: {account}")
        return

    masked_username = username[:2] + '****' + username[7:]
    print(f"[账号: {masked_username}] 开始执行")

    session = login(username, password)
    if not session:
        print(f"[账号: {masked_username}] 登录失败")
        return

    # 获取容量信息
    personal_used, personal_total = get_capacity(session, username)
    print(f"[账号: {masked_username}] 容量使用情况: 已用 {round(personal_used / 1024,2)}G / 总量 {round(personal_total/ 1024,2)}G")

    # 并发签到
    success_count = 0
    total_bonus = 0
    global time_now
    time_now = round(time.time(),3)
    #print(time_now)
    with concurrent.futures.ThreadPoolExecutor(max_workers=Max) as executor:
        futures = [executor.submit(sign_task, session, i + 1, username) for i in range(Max)]
        for future in concurrent.futures.as_completed(futures):
            success, bonus = future.result()
            if success:
                success_count += 1
                try:
                    total_bonus += int(bonus) if bonus else 0
                except (ValueError, TypeError):
                    total_bonus += 0
    print(f"[账号: {masked_username}] 签到结果: 成功 {success_count}/{Max} 次, 获得 {total_bonus}M 空间")

    # 获取容量信息
    personal_Used, personal_Total = get_capacity(session, username)
    detla_size = float(personal_Total) - float(personal_total)
    if detla_size == 0:
        print(f"[账号: {masked_username}] 今天已经签到过啦💔")
    else:
        print(f"[账号: {masked_username}] 实际容量提升: {detla_size}M / 总量 {round(personal_total/1024,2)}G")






def main():
    account_info = os.getenv('CtyunPan')
    if not account_info:
        print("请设置环境变量CtyunPan，格式为: 账号#密码&账号#密码")
        return

    # 分割多个账号(支持&和;分隔)
    accounts = []
    for sep in ['&', ';']:
        if sep in account_info:
            accounts.extend(acc.strip() for acc in account_info.split(sep) if '#' in acc)
            break
    else:
        if '#' in account_info:
            accounts.append(account_info.strip())

    if not accounts:
        print("未找到有效的账号信息，请检查环境变量格式(应为: 账号#密码&账号#密码)")
        return

    print(f"共检测到 {len(accounts)} 个账号")
    for i, account in enumerate(accounts, 1):
        print(f"\n===== 正在处理第 {i} 个账号 ======")
        process_account(account)


if __name__ == "__main__":
    print("===== 天翼云盘自动签到脚本开始运行 =====")
    start_time = time.time()
    main()
    end_time = time.time()
    print(f"\n===== 脚本执行完成, 总耗时: {end_time - start_time:.2f}秒 =====")